/*
 *  Copyright © OpenAtom Foundation.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.openatom.ubml.model.common.definition.cef.json;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.openatom.ubml.model.common.definition.cef.element.ElementDefaultVauleType;

import static com.fasterxml.jackson.core.JsonToken.END_ARRAY;
import static com.fasterxml.jackson.core.JsonToken.END_OBJECT;
import static com.fasterxml.jackson.core.JsonToken.FIELD_NAME;
import static com.fasterxml.jackson.core.JsonToken.START_ARRAY;
import static com.fasterxml.jackson.core.JsonToken.START_OBJECT;
import static com.fasterxml.jackson.core.JsonToken.VALUE_FALSE;
import static com.fasterxml.jackson.core.JsonToken.VALUE_NULL;
import static com.fasterxml.jackson.core.JsonToken.VALUE_NUMBER_FLOAT;
import static com.fasterxml.jackson.core.JsonToken.VALUE_NUMBER_INT;
import static com.fasterxml.jackson.core.JsonToken.VALUE_STRING;
import static com.fasterxml.jackson.core.JsonToken.VALUE_TRUE;

/**
 * The Utils Of Cef Serializer
 *
 * @ClassName: SerializerUtils
 * @Author: Benjamin Gong
 * @Date: 2021/1/11 17:13
 * @Version: V1.0
 */
public class SerializerUtils {
    //#region 读取Token
    public static void readStartObject(JsonParser parser) {
        JsonToken tokenType = parser.getCurrentToken();
        try {
            switch (tokenType) {
                case START_OBJECT:
                    parser.nextToken();
                    break;
                default:
                    String validateTypeNames = EnumSet.of(START_OBJECT).toString();
                    throwJsonTokenException(validateTypeNames, tokenType.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void readEndObject(JsonParser parser) {

        JsonToken tokenType = parser.getCurrentToken();
        try {
            switch (tokenType) {
                case END_OBJECT:
                    parser.nextToken();
                    break;
                default:
                    String validateTypeNames = EnumSet.of(END_OBJECT).toString();
                    throwJsonTokenException(validateTypeNames, tokenType.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void readStartArray(JsonParser parser) {
        JsonToken tokenType = parser.getCurrentToken();
        try {
            switch (tokenType) {
                case START_ARRAY:
                    parser.nextToken();
                    break;
                default:
                    String validateTypeNames = EnumSet.of(START_ARRAY).toString();
                    throwJsonTokenException(validateTypeNames, tokenType.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void readEndArray(JsonParser parser) {

        JsonToken tokenType = parser.getCurrentToken();
        try {
            switch (tokenType) {
                case END_ARRAY:
                    parser.nextToken();
                    break;
                default:
                    String validateTypeNames = EnumSet.of(END_ARRAY).toString();
                    throwJsonTokenException(validateTypeNames, tokenType.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static boolean readNullObject(JsonParser parser) {

        JsonToken tokenType = parser.getCurrentToken();
        try {
            switch (tokenType) {
                case VALUE_NULL:
                    parser.nextToken();
//                    JsonToken tokenType1=parser.getCurrentToken();
                    return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
    //#endregion

    //#region 读取基本类型
    public static String readPropertyName(JsonParser parser) {
        JsonToken tokenType = parser.getCurrentToken();
        if (tokenType != FIELD_NAME) {
            throwJsonTokenException(FIELD_NAME.toString(), tokenType.toString());
        }
        String propName = null;
        try {
            propName = parser.getCurrentName();
            parser.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return propName;
    }

    public static boolean readPropertyValue_boolean(JsonParser parser) {
        return readPropertyValue_boolean(parser, false);
    }

    public static boolean readPropertyValue_boolean(JsonParser parser, boolean defaultValue) {
        JsonToken tokenType = parser.getCurrentToken();
        boolean propValue = defaultValue;
        try {
            switch (tokenType) {
                case VALUE_NULL:
                    break;
                case VALUE_FALSE:
                    propValue = false;
                    break;
                case VALUE_TRUE:
                    propValue = true;
                    break;
                case VALUE_STRING:
                    propValue = Boolean.valueOf(parser.getValueAsString());
                default:
                    String validateTypeNames = EnumSet.of(VALUE_FALSE, VALUE_NULL, VALUE_TRUE, VALUE_STRING).toString();
                    throwJsonTokenException(validateTypeNames, tokenType.toString());
            }
            parser.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return propValue;
    }

    public static float readPropertyValue_Float(JsonParser parser) {
        return readPropertyValue_Float(parser, 0f);
    }

    public static int readPropertyValue_Integer(JsonParser parser, int defaultValue) {
        JsonToken tokenType = parser.getCurrentToken();
        int propValue = defaultValue;
        try {
            switch (tokenType) {
                case VALUE_NULL:
                    break;
                case VALUE_NUMBER_INT:
                    propValue = parser.getIntValue();
                    break;
                case VALUE_STRING:
                    propValue = Integer.valueOf(parser.getValueAsString());
                    break;
                default:
                    String validateTypeNames = EnumSet.of(VALUE_NUMBER_INT, VALUE_NULL, VALUE_STRING).toString();
                    throwJsonTokenException(validateTypeNames, tokenType.toString());
            }
            parser.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return propValue;
    }

    public static int readPropertyValue_Integer(JsonParser parser) {
        return readPropertyValue_Integer(parser, 0);
    }

    public static float readPropertyValue_Float(JsonParser parser, float defaultValue) {
        JsonToken tokenType = parser.getCurrentToken();
        float propValue = defaultValue;
        try {
            switch (tokenType) {
                case VALUE_NULL:
                    break;
                case VALUE_NUMBER_FLOAT:
                    propValue = parser.getFloatValue();
                    break;
                case VALUE_STRING:
                    propValue = Float.valueOf(parser.getValueAsString());
                    break;
                default:
                    String validateTypeNames = EnumSet.of(VALUE_NUMBER_FLOAT, VALUE_NULL, VALUE_STRING).toString();
                    throwJsonTokenException(validateTypeNames, tokenType.toString());
            }
            parser.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return propValue;
    }

    public static <T extends Enum<T>> T readPropertyValue_Enum(JsonParser parser, Class<T> enumClass, T[] enumValues) {
        return readPropertyValue_Enum(parser, enumClass, enumValues, null);
    }

    public static <T extends Enum<T>> T readPropertyValue_Enum(JsonParser parser, Class<T> enumClass, T[] enumValues, T defaultValue) {
        JsonToken tokenType = parser.getCurrentToken();
        T propValue = defaultValue;
        try {
            switch (tokenType) {
                case VALUE_NUMBER_INT:
                    int intValue = parser.getValueAsInt();
                    propValue = enumValues[intValue];
                    break;
                case VALUE_STRING:
                    String stringValue = parser.getValueAsString();
                    if (enumClass == ElementDefaultVauleType.class && stringValue.equals("Value")) {
                        stringValue = "Vaule";
                    }
                    propValue = T.valueOf(enumClass, stringValue);
                    break;
                default:
                    String validateTypeNames = EnumSet.of(VALUE_STRING, VALUE_STRING).toString();
                    throwJsonTokenException(validateTypeNames, tokenType.toString());
            }
            parser.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return propValue;
    }

    public static String readPropertyValue_String(JsonParser parser) {
        return readPropertyValue_String(parser, "");
    }

    public static <T> T readPropertyValue_Object(Class<T> targetClass, JsonParser parser) {
        try {
            return new ObjectMapper().readValue(parser, targetClass);
        } catch (IOException e) {
            throw new RuntimeException("类型" + targetClass.getTypeName() + "反序列化失败:" + parser.toString(), e);
        }
    }

    public static String readPropertyValue_String(JsonParser parser, String defaultValue) {
        JsonToken tokenType = parser.getCurrentToken();
        String propValue = defaultValue;
        try {
            switch (tokenType) {
                case VALUE_NULL:
                    break;
                case VALUE_STRING:
                    propValue = parser.getValueAsString(propValue);
                    break;
                default:
                    String validateTypeNames = EnumSet.of(VALUE_STRING, VALUE_NULL).toString();
                    throwJsonTokenException(validateTypeNames, tokenType.toString());
            }
            parser.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return propValue;
    }
    //#endregion

//    public static <T extends Enum<T>> EnumSet<T> readEnumSet(JsonParser parser, Class<T> type) {
//        EnumSetDeserializer der = new EnumSetDeserializer(SimpleType.constructUnsafe(type), new CefEnumDeserializer<T>(type));
//
//        EnumSet<T> propValue = EnumSet.noneOf(type);
//        try {
//            EnumSet<?> result = der.deserialize(parser, null);
//            Object[] array = result.();
//            for (int i = 0; i < array.length; i++) {
//                propValue.add(Array);
//            }
//            propValue = der.deserialize(parser, null);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return propValue;
//    }
//
// EnumSet反序列化供参考，json中存int

//    private EnumSet<ExecutingDataStatus> readGetExecutingDataStatus(JsonParser jsonParser) {
//        EnumSet<ExecutingDataStatus> result = EnumSet.noneOf(ExecutingDataStatus.class);
//        int intValueSum = SerializerUtils.readPropertyValue_Integer(jsonParser);
//        ExecutingDataStatus[] values = ExecutingDataStatus.values();
//        for (int i = values.length - 1; i >= 0; i--) {
//            ExecutingDataStatus value = values[i];
//            if (intValueSum > value.getValue()) {
//                result.add(value);
//                intValueSum -= value.getValue();
//            }
//        }
//        return result;
//    }

    public static ArrayList<String> readStringArray(JsonParser parser) {
        StringDeserializer der = new StringDeserializer();
        ArrayList<String> propValue = new ArrayList<String>();
        readArray(parser, der, propValue, true);
        return propValue;
    }

    public static <T extends Object, tArray extends ArrayList<T>> void readArray(JsonParser parser, JsonDeserializer<T> deserializer, tArray array) {
        readArray(parser, deserializer, array, false);
    }

    public static <T extends Object, tArray extends ArrayList<T>> void readArray(JsonParser parser, JsonDeserializer<T> deserializer, tArray array, boolean next) {
        if (readNullObject(parser)) {
            return;
        }
        readStartArray(parser);
        JsonToken tokentype = parser.getCurrentToken();
        if (tokentype != END_ARRAY) {
            while (parser.getCurrentToken() == tokentype) {
                try {
                    array.add(deserializer.deserialize(parser, null));
                    if (next) {
                        parser.nextToken();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        readEndArray(parser);
    }

    //#region 私有方法
    private static void throwJsonTokenException(String validateType, String currentType) {
        throw new RuntimeException(String.format("当前JsonToken应为'$1%s',实际为'$1%s'", validateType, currentType));
    }
    //#endregion

    /**
     * 序列化：写数组开始
     */
    public static void WriteStartArray(JsonGenerator generator) {
        try {
            generator.writeStartArray();
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WriteStartArray", e);
        }
    }

    /**
     * 序列化：写数组结束
     */
    public static void WriteEndArray(JsonGenerator generator) {
        try {
            generator.writeEndArray();
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WriteEndArray", e);
        }
    }

    /**
     * 序列化：写对象开始
     */
    public static void writeStartObject(JsonGenerator generator) {
        try {
            generator.writeStartObject();
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WriteStartObject", e);
        }
    }

    /**
     * 序列化：写对象结束
     */
    public static void writeEndObject(JsonGenerator generator) {
        try {
            generator.writeEndObject();
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WriteEndObject", e);
        }
    }

    /**
     * 序列化：写入键值对
     */
    public static void writePropertyValue(JsonGenerator generator, String propertyName, String propertyValue) {
        writePropertyName(generator, propertyName);
        writePropertyValue_String(generator, propertyValue);
    }

    /**
     * 序列化：写入键值对
     */
    public static void writePropertyValue(JsonGenerator generator, String propertyName, Object propertyValue) {
        writePropertyName(generator, propertyName);
        writePropertyValue_Object(generator, propertyValue);
//        writePropertyValue_String(generator,propertyValue);
    }

    /**
     * 序列化：写入属性值
     */
    public static void writePropertyName(JsonGenerator generator, String propertyName) {
//        generator.setCurrentValue(propertyName)
        try {
            generator.writeFieldName(propertyName);
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WritePropertyName", e);
        }
    }

    /**
     * 序列化，写入基本类型（boolean）
     */
    public static void writePropertyValue_boolean(JsonGenerator generator, boolean propertyValue) {
        try {
            generator.writeBoolean(propertyValue);
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WritePropertyValue_boolean", e);
        }
    }

    /**
     * 序列化，写入基本类型（Integer）
     */
    public static void writePropertyValue_Integer(JsonGenerator generator, Integer propertyValue) {
        try {
            generator.writeNumber(propertyValue);
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WritePropertyValue_Integer", e);
        }
    }

    public static void writePropertyValue_Number(JsonGenerator generator, Integer propertyValue) {
        try {
            generator.writeNumber(propertyValue);
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WritePropertyValue_Integer", e);
        }
    }

    public static void writePropertyValue_Number(JsonGenerator generator, BigDecimal propertyValue) {
        try {
            generator.writeNumber(propertyValue);
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WritePropertyValue_BigDecimal", e);
        }
    }

    public static void writePropertyValue_Number(JsonGenerator generator, float propertyValue) {
        try {
            generator.writeNumber(propertyValue);
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WritePropertyValue_Float", e);
        }
    }

    /**
     * 序列化，写入基本类型（Float）
     */
    public static void writePropertyValue_Float(JsonGenerator generator, float propertyValue) {
        try {
            generator.writeNumber(propertyValue);
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WritePropertyValue_Float", e);
        }
    }

    /**
     * 序列化，写入基本类型（String）
     */
    public static void writePropertyValue_String(JsonGenerator generator, String propertyValue) {
        try {
            generator.writeString(propertyValue);
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("WritePropertyValue_String", e);
        }
    }

    public static void writePropertyValue_Null(JsonGenerator generator) {
        try {
            generator.writeNull();
        } catch (IOException e) {
            e.printStackTrace();
            throwJsonGeneratorException("writePropertyValue_Null", e);
        }
    }

    /**
     * 序列化，写入基本类型（Object）
     */
    public static void writePropertyValue_Object(JsonGenerator generator, Object propertyValue) {
        try {
            generator.writeObject(propertyValue);
        } catch (IOException e) {
            throwJsonGeneratorException(propertyValue.toString(), e);
        }
    }

    public static <T extends Object, tList extends List<T>> void writeArray(JsonGenerator jsonGenerator, JsonSerializer serializer, tList list) {
        //void writeArray(JsonGenerator jsonGenerator, JsonSerializer<T> serializer, TArray array){
        SerializerUtils.WriteStartArray(jsonGenerator);
        for (T item : list) {
            try {
                serializer.serialize(item, jsonGenerator, null);
            } catch (IOException e) {
                e.printStackTrace();
                throwJsonGeneratorException("WriteArray", e);
            }
        }
        SerializerUtils.WriteEndArray(jsonGenerator);
    }

    private static void throwJsonGeneratorException(String curMethod, Exception e) {
        throw new RuntimeException("当前序列化" + curMethod + "出现异常！", e);
    }
}
